
package v2store

import (
	"encoding/json"
	"fmt"
	"path"
	"strconv"
	"strings"
	"sync"
	"time"

	"go.etcd.io/etcd/client/pkg/v3/types"
	"go.etcd.io/etcd/server/v3/etcdserver/api/v2error"

	"github.com/jonboulle/clockwork"
)

// 首次初始化存储时要设置的默认版本。
const defaultVersion = 2

var minExpireTime time.Time

func init() {
	minExpireTime, _ = time.Parse(time.RFC3339, "2000-01-01T00:00:00Z")
}

type Store interface {
	Version() int
	Index() uint64

	Get(nodePath string, recursive, sorted bool) (*Event, error)
	Set(nodePath string, dir bool, value string, expireOpts TTLOptionSet) (*Event, error)
	Update(nodePath string, newValue string, expireOpts TTLOptionSet) (*Event, error)
	Create(nodePath string, dir bool, value string, unique bool,
		expireOpts TTLOptionSet) (*Event, error)
	CompareAndSwap(nodePath string, prevValue string, prevIndex uint64,
		value string, expireOpts TTLOptionSet) (*Event, error)
	Delete(nodePath string, dir, recursive bool) (*Event, error)
	CompareAndDelete(nodePath string, prevValue string, prevIndex uint64) (*Event, error)

	Watch(prefix string, recursive, stream bool, sinceIndex uint64) (Watcher, error)

	Save() ([]byte, error)
	Recovery(state []byte) error

	Clone() Store
	SaveNoCopy() ([]byte, error)

	JsonStats() []byte
	DeleteExpiredKeys(cutoff time.Time)

	HasTTLKeys() bool
}

type TTLOptionSet struct {
	ExpireTime time.Time
	Refresh    bool
}

// Store接口是v2存储对外暴露的API接口，其中定义了v2存储的主要行为。
type store struct {
	// v2存储是纯内存实现，它以树形结构将全部数据维护在内存中，
	// 树中的每个节点都是node实例。该字段记录了此树型结构的根节点。
	Root           *node
	// watcherHub的主要功能是管理客户端添加的watcher监昕和Event实例，
	WatcherHub     *watcherHub	//
	// 该字段是修改操作的唯一标识，每出现一次修改操作，该字段就会自增一次。
	CurrentIndex   uint64
	Stats          *Stats
	CurrentVersion int
	// ttlKeyHeap的主要功能是将全部节点按照过期时间进行排序，形成一个最小堆。
	ttlKeyHeap     *ttlKeyHeap  // 需要手动恢复
	// 在store进行任何操作之前，都需要获取该锁进行同步。
	worldLock      sync.RWMutex //
	clock          clockwork.Clock
	// 记录了哪些节点是只读节点，这些节点都无法被修改。
	readonlySet    types.Set
}

// 新建创建一个存储，其中给定的名称空间将被创建为初始目录。
func New(namespaces ...string) Store {
	s := newStore(namespaces...)
	s.clock = clockwork.NewRealClock()
	return s
}

func newStore(namespaces ...string) *store {
	s := new(store)
	s.CurrentVersion = defaultVersion
	s.Root = newDir(s, "/", s.CurrentIndex, nil, Permanent)
	for _, namespace := range namespaces {
		s.Root.Add(newDir(s, namespace, s.CurrentIndex, s.Root, Permanent))
	}
	s.Stats = newStats()
	s.WatcherHub = newWatchHub(1000)
	s.ttlKeyHeap = newTtlKeyHeap()
	s.readonlySet = types.NewUnsafeSet(append(namespaces, "/")...)
	return s
}

// 版本检索存储的当前版本。
func (s *store) Version() int {
	return s.CurrentVersion
}

// 索引检索存储的当前索引。
func (s *store) Index() uint64 {
	s.worldLock.RLock()
	defer s.worldLock.RUnlock()
	return s.CurrentIndex
}

// 主要功能就是在树形结构中查找指定路径对应的node节点，
// 其中会根据recursive参数和sorted参数决定是否加载子节点，以及是否对子节点进行排序，
func (s *store) Get(nodePath string, recursive, sorted bool) (*Event, error) {
	var err *v2error.Error

	s.worldLock.RLock()
	defer s.worldLock.RUnlock()

	defer func() {
		if err == nil {
			s.Stats.Inc(GetSuccess)
			if recursive {
				reportReadSuccess(GetRecursive)
			} else {
				reportReadSuccess(Get)
			}
			return
		}

		s.Stats.Inc(GetFail)
		if recursive {
			reportReadFailure(GetRecursive)
		} else {
			reportReadFailure(Get)
		}
	}()
	// 根据nodePath获取对应node节点
	n, err := s.internalGet(nodePath)
	if err != nil {
		return nil, err
	}
	// 创建Event实例
	e := newEvent(Get, nodePath, n.ModifiedIndex, n.CreatedIndex)
	e.EtcdIndex = s.CurrentIndex
	// 如果待查找节点是目录节点，则获取子节点；如采是KV节点，则加载其Value值
	e.Node.loadInternalNode(n, recursive, sorted, s.clock)

	return e, nil
}

// store.Create（）方法对外提供了创建node节点的功能，在其创建指定节点的过程中，
// 还会同时创建中间不存在的目录节点（这些目录节点会被设置成永久的）。
func (s *store) Create(nodePath string, dir bool, value string, unique bool, expireOpts TTLOptionSet) (*Event, error) {
	var err *v2error.Error

	s.worldLock.Lock()
	defer s.worldLock.Unlock()

	defer func() {
		if err == nil {
			s.Stats.Inc(CreateSuccess)
			reportWriteSuccess(Create)
			return
		}

		s.Stats.Inc(CreateFail)
		reportWriteFailure(Create)
	}()
	// 创建目标节点，同时会创建中间涉及的目录节点
	e, err := s.internalCreate(nodePath, dir, value, unique, false, expireOpts.ExpireTime, Create)
	if err != nil {
		return nil, err
	}
	// 设置Event.Etcdindex
	e.EtcdIndex = s.CurrentIndex
	// 将Event添加到EventHistory中，同时触发相关的watcher
	s.WatcherHub.notify(e)

	return e, nil
}

// store.Set（）方法对外提供了创建（或更新）node节点的功能，
// 如果指定的节点己存在，则会将其替换。
// store.Set（）方法首先会调用internalGet（）方法查找指定节点，
// 然后根据expireOpts参数决定此次修改操作的类型，之后调用internalCreate（）方法替换现有节点，
// 最后将此次操作的相关信息封装成Event实例保存，井决定是否触发相关watcher实例。
func (s *store) Set(nodePath string, dir bool, value string, expireOpts TTLOptionSet) (*Event, error) {
	var err *v2error.Error

	s.worldLock.Lock()
	defer s.worldLock.Unlock()

	defer func() {
		if err == nil {
			s.Stats.Inc(SetSuccess)
			reportWriteSuccess(Set)
			return
		}

		s.Stats.Inc(SetFail)
		reportWriteFailure(Set)
	}()

	// 通过internalGet（）方法获取指定节点
	n, getErr := s.internalGet(nodePath)
	// 处理工nternalGet（）方法在操作过程中遇到的异常，注意，这里会忽略EcodeKeyNotFound异常
	if getErr != nil && getErr.ErrorCode != v2error.EcodeKeyNotFound {
		err = getErr
		return nil, err
	}
	// 根据此次操作类型，决定最终的节点值
	if expireOpts.Refresh {
		// 异常处理，不会忽略EcodeKeyNotFound异常，因为节点都没找到，所以无法获取其原值
		if getErr != nil {
			err = getErr
			return nil, err
		}
		// 如采是Refresh类型的修改操作，则不会改变节点的位
		value = n.Value
	}

	// 通过internalCreate（）方法创建指定的节点，
	// 这里需要注意其replace参数，该值被设置为true
	e, err := s.internalCreate(nodePath, dir, value, false, true, expireOpts.ExpireTime, Set)
	if err != nil {
		return nil, err
	}
	e.EtcdIndex = s.CurrentIndex

	// 设立此次修改操作对应Event的PrevNode字段，将其指向修改之前的节点
	if getErr == nil {
		prev := newEvent(Get, nodePath, n.ModifiedIndex, n.CreatedIndex)
		prev.Node.loadInternalNode(n, false, false, s.clock)
		e.PrevNode = prev.Node
	}

	if !expireOpts.Refresh {
		// 如果此次修改操作不是Refresh类型的，则触发相关的watcher
		s.WatcherHub.notify(e)
	} else {
		// 此次修改操作为Refresh类型，则不会触发任何watcher，而只是将其添加到EventHistory中保存
		e.SetRefresh()
		s.WatcherHub.add(e)
	}

	return e, nil
}

// 返回用户可读的比较失败原因
func getCompareFailCause(n *node, which int, prevValue string, prevIndex uint64) string {
	switch which {
	case CompareIndexNotMatch:
		return fmt.Sprintf("[%v != %v]", prevIndex, n.ModifiedIndex)
	case CompareValueNotMatch:
		return fmt.Sprintf("[%v != %v]", prevValue, n.Value)
	default:
		return fmt.Sprintf("[%v != %v] [%v != %v]", prevValue, n.Value, prevIndex, n.ModifiedIndex)
	}
}

// store. CompareAndSwap（）方法对外提供了CAS操作，其主要流程是先查找待处理节点，
// 然后比较节点的当前值与传入的prevValue，同时会比较当前节点的Modifiedindex与传入的prevlndex，
// 如果相等则表示当前节点没有被修改过，此时就会对节点的值进行修改；
// 如果不相等则表示当前节点己经被别人修改过，此时不应该对其进行修改。
// prevValue参数，调用者认为目标节点当前值应该是 prevValue，
// 如果当前节点被修改过，则不再是prevValue，则调用者不能对其进行修改；prevIndex同理
func (s *store) CompareAndSwap(nodePath string, prevValue string, prevIndex uint64,
	value string, expireOpts TTLOptionSet) (*Event, error) {

	var err *v2error.Error

	s.worldLock.Lock()
	defer s.worldLock.Unlock()

	defer func() {
		if err == nil {
			s.Stats.Inc(CompareAndSwapSuccess)
			reportWriteSuccess(CompareAndSwap)
			return
		}

		s.Stats.Inc(CompareAndSwapFail)
		reportWriteFailure(CompareAndSwap)
	}()

	nodePath = path.Clean(path.Join("/", nodePath))
	// 检测待删除节点是否为只读的
	if s.readonlySet.Contains(nodePath) {
		return nil, v2error.NewError(v2error.EcodeRootROnly, "/", s.CurrentIndex)
	}
	// 调用internalGet（）方法获取待处理的节点
	n, err := s.internalGet(nodePath)
	if err != nil {
		return nil, err
	}
	if n.IsDir() { // 检测目标节点是否为目录节点，CAS操作只能修改KV节点
		err = v2error.NewError(v2error.EcodeNotFile, nodePath, s.CurrentIndex)
		return nil, err
	}

	// 比较目标节点的位和prevValue，同时也会比较当前节点的Modifiedindex和previndex
	if ok, which := n.Compare(prevValue, prevIndex); !ok {
		// 如果任意一个字段不相等，则无法更新，返回异常
		cause := getCompareFailCause(n, which, prevValue, prevIndex)
		err = v2error.NewError(v2error.EcodeTestFailed, cause, s.CurrentIndex)
		return nil, err
	}
	// 若上述两个比较完全相等，则继续后面的修改操作
	if expireOpts.Refresh {	// 如果此次更新是Refresh类型，则节点的值不变
		value = n.Value
	}

	// 递增store.Currentindex
	s.CurrentIndex++
	// 创建此次CAS操作对应的Event实例，并更新其字段
	e := newEvent(CompareAndSwap, nodePath, s.CurrentIndex, n.CreatedIndex)
	e.EtcdIndex = s.CurrentIndex
	e.PrevNode = n.Repr(false, false, s.clock)
	eNode := e.Node

	// 更新节点位
	if err := n.Write(value, s.CurrentIndex); err != nil {
		return nil, err
	}
	// 更新节点的过期时间
	n.UpdateTTL(expireOpts.ExpireTime)

	// 复制安全值
	valueCopy := value
	eNode.Value = &valueCopy
	eNode.Expiration, eNode.TTL = n.expirationAndTTL(s.clock)
	// 根据更新操作是否为Refresh类型，决定是否触发相应的watcher
	if !expireOpts.Refresh {
		s.WatcherHub.notify(e)
	} else {
		e.SetRefresh()
		s.WatcherHub.add(e)
	}

	return e, nil
}

// store.Delete（）方法对外提供了删除指定node节点的功能。如果待删除节点是目录节点，
// 则需要将recursive参数设置为true，才能递归删除其子节点，并最终删除该目录节点。
func (s *store) Delete(nodePath string, dir, recursive bool) (*Event, error) {
	var err *v2error.Error

	s.worldLock.Lock()
	defer s.worldLock.Unlock()

	defer func() {
		if err == nil {
			s.Stats.Inc(DeleteSuccess)
			reportWriteSuccess(Delete)
			return
		}

		s.Stats.Inc(DeleteFail)
		reportWriteFailure(Delete)
	}()

	nodePath = path.Clean(path.Join("/", nodePath))
	// 如果节点不存在，我们不允许用户更改“/”
	if s.readonlySet.Contains(nodePath) {
		return nil, v2error.NewError(v2error.EcodeRootROnly, "/", s.CurrentIndex)
	}

	// 递归暗示目录
	if recursive {
		dir = true
	}
	// 调用工nternalGet（）方法获取待删除的节点
	n, err := s.internalGet(nodePath)
	if err != nil { // ”，返回错误
		return nil, err
	}

	nextIndex := s.CurrentIndex + 1
	// 创建此次删除操作对应的Event实例
	e := newEvent(Delete, nodePath, nextIndex, n.CreatedIndex)
	e.EtcdIndex = nextIndex
	e.PrevNode = n.Repr(false, false, s.clock)
	eNode := e.Node

	if n.IsDir() {
		eNode.Dir = true
	}

	callback := func(path string) { // 回调函数
		// 用已删除的设置true通知观察者
		s.WatcherHub.notifyWatchers(e, path, true)
	}
	// 删除指定节点，如果是目录节点，则会根据recurs工ve参数决定是否删除子节点，
	// 并在删除过程中调用回调函数，其中会触发相关watcher
	err = n.Remove(dir, recursive, callback)
	if err != nil {
		return nil, err
	}

	// 递增store.Currentindex值
	s.CurrentIndex++
	// 触发相关watcher
	s.WatcherHub.notify(e)
	return e, nil
}

func (s *store) CompareAndDelete(nodePath string, prevValue string, prevIndex uint64) (*Event, error) {
	var err *v2error.Error

	s.worldLock.Lock()
	defer s.worldLock.Unlock()

	defer func() {
		if err == nil {
			s.Stats.Inc(CompareAndDeleteSuccess)
			reportWriteSuccess(CompareAndDelete)
			return
		}

		s.Stats.Inc(CompareAndDeleteFail)
		reportWriteFailure(CompareAndDelete)
	}()

	nodePath = path.Clean(path.Join("/", nodePath))

	n, err := s.internalGet(nodePath)
	if err != nil { // 如果节点不存在，返回错误
		return nil, err
	}
	if n.IsDir() { // 只能比较和删除文件
		return nil, v2error.NewError(v2error.EcodeNotFile, nodePath, s.CurrentIndex)
	}

	// 如果同时给出prevValue和prevIndex，我们将对它们进行测试。
	// 只有在两个测试都成功的情况下，才会执行命令。
	if ok, which := n.Compare(prevValue, prevIndex); !ok {
		cause := getCompareFailCause(n, which, prevValue, prevIndex)
		return nil, v2error.NewError(v2error.EcodeTestFailed, cause, s.CurrentIndex)
	}

	// 更新etcd索引
	s.CurrentIndex++

	e := newEvent(CompareAndDelete, nodePath, s.CurrentIndex, n.CreatedIndex)
	e.EtcdIndex = s.CurrentIndex
	e.PrevNode = n.Repr(false, false, s.clock)

	callback := func(path string) { // 通知函数
		// 用已删除的设置值通知观察者
		s.WatcherHub.notifyWatchers(e, path, true)
	}

	err = n.Remove(false, false, callback)
	if err != nil {
		return nil, err
	}

	s.WatcherHub.notify(e)

	return e, nil
}

func (s *store) Watch(key string, recursive, stream bool, sinceIndex uint64) (Watcher, error) {
	s.worldLock.RLock()
	defer s.worldLock.RUnlock()

	key = path.Clean(path.Join("/", key))
	if sinceIndex == 0 {
		sinceIndex = s.CurrentIndex + 1
	}
	// 观察者不知道当前索引，因此我们需要在
	w, err := s.WatcherHub.watch(key, recursive, stream, sinceIndex, s.CurrentIndex)
	if err != nil {
		return nil, err
	}

	return w, nil
}

// walk遍历所有节点路径并在每个目录上应用walkFunc 
func (s *store) walk(nodePath string, walkFunc func(prev *node, component string) (*node, *v2error.Error)) (*node, *v2error.Error) {
	components := strings.Split(nodePath, "/")
	curr := s.Root	// 从Root节点开始查找
	var err *v2error.Error
	for i := 1; i < len(components); i++ {
		if len(components[i]) == 0 { // 忽略空路径
			return curr, nil
		}
		// 通过walkFunc（）函数查找curr节点的指定子节点
		curr, err = walkFunc(curr, components[i])
		if err != nil {
			return nil, err
		}
	}
	return curr, nil
}

// store.Update（）方法对外提供了更新node节点的功能。
// 如果待更新节点是一个键值对节点，那么我们可以同时更新其Value值和过期时间。
// 如果待更新的节点时一个目录节点，那么我们只能更新其过期时间。
func (s *store) Update(nodePath string, newValue string, expireOpts TTLOptionSet) (*Event, error) {
	var err *v2error.Error

	s.worldLock.Lock()
	defer s.worldLock.Unlock()

	defer func() {
		if err == nil {
			s.Stats.Inc(UpdateSuccess)
			reportWriteSuccess(Update)
			return
		}

		s.Stats.Inc(UpdateFail)
		reportWriteFailure(Update)
	}()

	nodePath = path.Clean(path.Join("/", nodePath))	// 整理路径
	// 检测待更新节点是否为只读的
	if s.readonlySet.Contains(nodePath) {
		return nil, v2error.NewError(v2error.EcodeRootROnly, "/", s.CurrentIndex)
	}
	currIndex, nextIndex := s.CurrentIndex, s.CurrentIndex+1
	// 调用internalGet（）方法，查找待更新节点，
	n, err := s.internalGet(nodePath)
	if err != nil { // 如果节点不存在，则返回错误
		return nil, err
	}
	if n.IsDir() && len(newValue) != 0 {
		// 如果节点是目录，我们无法将值更新为非空
		return nil, v2error.NewError(v2error.EcodeNotFile, nodePath, currIndex)
	}
	// 如果此次操作是Refresh类型的，则其Value位不会更新，只会更新其过期时间
	if expireOpts.Refresh {
		newValue = n.Value
	}
	// 创建此次操作对应的Event实例，并更新相关字段
	e := newEvent(Update, nodePath, nextIndex, n.CreatedIndex)
	e.EtcdIndex = nextIndex
	e.PrevNode = n.Repr(false, false, s.clock)
	eNode := e.Node
	// 更新节点的Value值和Modifiedindex字段
	if err := n.Write(newValue, nextIndex); err != nil {
		return nil, fmt.Errorf("nodePath %v : %v", nodePath, err)
	}
	// ／检测待史新节点是否为目录节点，如采走，则不能更新其Value
	if n.IsDir() {
		eNode.Dir = true
	} else {
		// 复制安全
		newValueCopy := newValue
		eNode.Value = &newValueCopy
	}

	// 更新节点的过期时间和对应Event实例中的TTL
	n.UpdateTTL(expireOpts.ExpireTime)
	eNode.Expiration, eNode.TTL = n.expirationAndTTL(s.clock)
	// 此次修改操作不是Refresh类型，如l触发相关watcher
	if !expireOpts.Refresh {
		s.WatcherHub.notify(e)
	} else {	// 此次修改操作是Refresh类型，则不会触发相关watcher，只是将其添加到EventHistory中
		e.SetRefresh()
		s.WatcherHub.add(e)
	}
	// 更新store.Currentindex字段
	s.CurrentIndex = nextIndex

	return e, nil
}

// store. internalCreate（）方法是创建节点的核心
// 各个参数的含义:
// nodePath：待创建节点的完整路径。
// dir：此次创建的节点是否为目录节点。
// value：如果此次创建的节点为键值对节点，则value为其值。
// unique：是否要创建一个唯一节点。
// replace：待创建的节点己存在，是否要对其进行替换。注意，这里只能替换己存在的键值对节点，不能替换己存在的目录节点。
// expireTime：待创建节点的过期时间。
func (s *store) internalCreate(nodePath string, dir bool, value string, unique, replace bool,
	expireTime time.Time, action string) (*Event, *v2error.Error) {

	currIndex, nextIndex := s.CurrentIndex, s.CurrentIndex+1

	if unique { // 在节点路径
		// 根据unique参数决定是否创建唯一节点，其方式在指定路径下，以nextIndex为名称创建一个节点
		nodePath += "/" + fmt.Sprintf("%020s", strconv.FormatUint(nextIndex, 10))
	}
	nodePath = path.Clean(path.Join("/", nodePath))	// 整理路径

	// 检测待创建节点是否在只读路径下，如果是，则抛出异常（
	if s.readonlySet.Contains(nodePath) {
		return nil, v2error.NewError(v2error.EcodeRootROnly, "/", currIndex)
	}

	// 设置节点的过期时间
	if expireTime.Before(minExpireTime) {
		expireTime = Permanent
	}
	// 切分路径得到父节点路径及待创建节点的名称
	dirName, nodeName := path.Split(nodePath)

	// 追历整个路径，如果路径中有目录节点不存在，则创建该目录节点，
	// 注意，这里的返回位是待创建节点的父节点，例如，
	// 待创建节点是“/foo/bar/tom”，则此处返回值“/foo/bar＂节点，
	d, err := s.walk(dirName, s.checkDir)
	if err != nil {
		s.Stats.Inc(SetFail)
		reportWriteFailure(action)
		err.Index = currIndex
		return nil, err
	}
	// 创建此次操作对应的Event实例
	e := newEvent(action, nodePath, nextIndex, nextIndex)
	eNode := e.Node
	// 查找待创建节点
	n, _ := d.GetChild(nodeName)

	// 如果待创建节点已经存在，则根据replace参数决定是否替换已存在的节点
	if n != nil {
		if replace {
			if n.IsDir() {
				return nil, v2error.NewError(v2error.EcodeNotFile, nodePath, currIndex)
			}
			// 将已存在节点的信息记录到Event.PrevNode中
			e.PrevNode = n.Repr(false, false, s.clock)
			// 删除已存在的节点
			if err := n.Remove(false, false, nil); err != nil {
				return nil, err
			}
		} else {	// 如果不替换已存在的节点，则直接返回异常
			return nil, v2error.NewError(v2error.EcodeNodeExist, nodePath, currIndex)
		}
	}

	if !dir { // 根据dir参数决定创建KV节点还是目录节点
		valueCopy := value
		eNode.Value = &valueCopy
		n = newKV(s, nodePath, value, nextIndex, d, expireTime)
	} else { // 创建目录节点
		eNode.Dir = true
		n = newDir(s, nodePath, nextIndex, d, expireTime)
	}

	// 将创建好的节点添加到父节点中
	if err := d.Add(n); err != nil {
		return nil, err
	}

	// 如果新建节点是非永久节点，则将其记录到ttlKeyHeap中
	if !n.IsPermanent() {
		s.ttlKeyHeap.push(n)
		// 更新Event中的过期时间和存活时间
		eNode.Expiration, eNode.TTL = n.expirationAndTTL(s.clock)
	}
	// 递增Currentindex
	s.CurrentIndex = nextIndex
	return e, nil
}

// internalGet() 方法中，会根据给定的路径从Root节点逐层查找，直至查找到目标node节点
func (s *store) internalGet(nodePath string) (*node, *v2error.Error) {
	nodePath = path.Clean(path.Join("/", nodePath))	// 整理路径格式
	// 定义walkFunc（）方法，其功能是在parent节点下查找指定子节点，若查找失败，则返回异常
	walkFunc := func(parent *node, name string) (*node, *v2error.Error) {
		// 检测parent节点是否为目录节点
		if !parent.IsDir() {
			err := v2error.NewError(v2error.EcodeNotDir, parent.Path, s.CurrentIndex)
			return nil, err
		}
		// 查找指定的子节点并返回，查找失败，则返回异常
		child, ok := parent.Children[name]
		if ok {
			return child, nil
		}
		return nil, v2error.NewError(v2error.EcodeKeyNotFound, path.Join(parent.Path, name), s.CurrentIndex)
	}
	// 真正执行walkFunc（）方法，逐层查找节点的地方
	f, err := s.walk(nodePath, walkFunc)
	if err != nil {
		return nil, err
	}
	return f, nil
}

// store.DeleteExpiredKeys（）方法的主要功能是删除过期节点。
// DeleteExpiredKeys（）方法通过其维护的最小堆来按序清理过期节点。
func (s *store) DeleteExpiredKeys(cutoff time.Time) {
	s.worldLock.Lock()
	defer s.worldLock.Unlock()

	for {
		node := s.ttlKeyHeap.top()	// 获取过期时间最近的节点
		if node == nil || node.ExpireTime.After(cutoff) {
			break	// 整个存储中都没有过期节点，则直接结束
		}

		s.CurrentIndex++	// 递增store.Currentindex
		// 创建此次操作对应的Event实例
		e := newEvent(Expire, node.Path, s.CurrentIndex, node.CreatedIndex)
		e.EtcdIndex = s.CurrentIndex
		e.PrevNode = node.Repr(false, false, s.clock)
		if node.IsDir() {	// 检测过期节点是否为目录节点
			e.Node.Dir = true
		}

		callback := func(path string) { // 回调函数，负责触发相关的watcher
			s.WatcherHub.notifyWatchers(e, path, true)
		}

		s.ttlKeyHeap.pop()	// 将过期节点从ttlKeyHeap中删除
		// 将过期节点从store中删除（击。采是目录节点，则递归删除其子节点）
		node.Remove(true, true, callback)

		reportExpiredKey()
		s.Stats.Inc(ExpireCount)

		s.WatcherHub.notify(e)
	}

}

// store.checkDir（），该方法负责查找指定节点下的指定子节点，
// 如果子节点不存在，则创建对应的目录节点。
func (s *store) checkDir(parent *node, dirName string) (*node, *v2error.Error) {
	// 在父节点中查找指定的子节点
	node, ok := parent.Children[dirName]
	if ok {
		if node.IsDir() {	// 如果自己子节点是目录节点，则返回子节点
			return node, nil
		}
		// 如果子节点不是目录节点，则直接返回异常，因为无法在此节点下新建其他节点
		return nil, v2error.NewError(v2error.EcodeNotDir, node.Path, s.CurrentIndex)
	}
	// 如果没有查找到对应节点，则创建对应的目录节点，并添加到父节点中
	n := newDir(s, path.Join(parent.Path, dirName), s.CurrentIndex+1, parent, Permanent)
	parent.Children[dirName] = n
	// 返回的依然是子节点
	return n, nil
}

// 负责将整个store实例序列化成JSON格式
func (s *store) Save() ([]byte, error) {
	b, err := json.Marshal(s.Clone())
	if err != nil {
		return nil, err
	}

	return b, nil
}

func (s *store) SaveNoCopy() ([]byte, error) {
	b, err := json.Marshal(s)
	if err != nil {
		return nil, err
	}

	return b, nil
}

func (s *store) Clone() Store {
	s.worldLock.RLock()

	clonedStore := newStore()
	clonedStore.CurrentIndex = s.CurrentIndex
	clonedStore.Root = s.Root.Clone()
	clonedStore.WatcherHub = s.WatcherHub.clone()
	clonedStore.Stats = s.Stats.clone()
	clonedStore.CurrentVersion = s.CurrentVersion

	s.worldLock.RUnlock()
	return clonedStore
}

// 负责将JSON格式的数据反序列化成store实例
func (s *store) Recovery(state []byte) error {
	s.worldLock.Lock()
	defer s.worldLock.Unlock()
	err := json.Unmarshal(state, s)

	if err != nil {
		return err
	}

	s.ttlKeyHeap = newTtlKeyHeap()

	s.Root.recoverAndclean()
	return nil
}

func (s *store) JsonStats() []byte {
	s.Stats.Watchers = uint64(s.WatcherHub.count)
	return s.Stats.toJson()
}

func (s *store) HasTTLKeys() bool {
	s.worldLock.RLock()
	defer s.worldLock.RUnlock()
	return s.ttlKeyHeap.Len() != 0
}
